Skip to content

ci(stale): correct the action SHA the job has never once resolved - #8463

Draft
baozhoutao wants to merge 1 commit into
mainfrom
claude/issue-8126-stale-yml
Draft

ci(stale): correct the action SHA the job has never once resolved#8463
baozhoutao wants to merge 1 commit into
mainfrom
claude/issue-8126-stale-yml

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes #8126

One line of behaviour changes: the pinned SHA. Everything else in this diff is the comment that records why.

The real cause, read from the log rather than guessed

The card named the pinned action ref as the first place to look and explicitly did not claim it was the cause. It is. Read verbatim from run 34172858995:

##[error]Unable to resolve action `actions/stale@e00e804f6792d3fedb5bd3a27df2761c5f86c981`, unable to find version `e00e804f6792d3fedb5bd3a27df2761c5f86c981`

e00e804f… is not actions/stale's v9.0.0 commit and is not any commit in that repository. The comment beside it (# v9.0.0) was right all along — the version exists, the SHA was fiction. The job never reached actions/stale; the 1-4 seconds in the card measure how fast it fails to begin.

Re-derived on today's tree, and the instrument checked first

reading value how
completed schedule runs 236 Actions API, 2026-09-08T00:35Z (card said 234 on 09-06; two nightlies since)
run #1 21052154491, 2026-01-16T01:14:37Z, failure oldest page of the same listing
run #236 34172858995, 2026-09-08T00:17:51Z, failure newest page
successes 0 the card's ?status=success read, with labeler.yml = 5448 as its control. ⚠️ Declared limitation: the MCP Actions surface exposes status but not conclusion, so I could not re-run that exact filter myself — what I add instead is that the first and the last run both failed and that the ref is provably unresolvable today

⚠️ git log -S cannot date the bad line. The oldest commit this checkout can reach for stale.yml is the shallow boundary (9969e9f63, in .git/shallow, parent unknown), so it reads as the file's creation and is not — the run history proves the file predates it by six months.

The replacement SHA is measured, not looked up

actions/stale is not reachable from this session's API surface, so guessing a SHA would have reproduced the exact defect one layer up. Instead, ten candidate refs were probed as ten independent jobs in one run (34173534757) on a throwaway branch, every one of them a dry run (debug-only: true, days-before-*: -1, read-only token):

ref Set up job
actions/checkout@v7 — positive control success
actions/stale@e00e804f… — negative control, the ref on main failure
actions/stale@main, @master, @v8, @v9, @v9.0.0, @v10, @v11 success
actions/stale@v12 failure (does not exist)

The runner printed the mapping itself, which is where the new SHA comes from:

Download action repository 'actions/stale@v9.0.0' (SHA:28ca1036281a5e5922ead5184a1bbf96e5fc984e)

v9.0.0 is kept because it is the version this file already declares. A version bump is a separate decision, not something to smuggle in behind a repair.

What turning it on does — measured before turning it on

This file with only the SHA changed, plus debug-only: true and a read-only token, ran green: 34173608021 — the first successful run in this workflow's life. It swept the open board to exhaustion (No more issues found to process):

Processed items: 466
├── Processed issues: 457
└── Processed PRs   : 9
Fetched items: 466
Operations performed: 6

No stale counter, no label counter, no comment counter. Nothing would be marked and nothing would be closed.

A zero is worthless without a control, so the same file was run again with the thresholds dropped to 0 (34173673214) — the block gains exactly the lines that were absent:

New stale items: 125
├── New stale issues: 124
└── New stale PRs   : 1
Added items labels: 125
Added items comments: 125

So the zero is a reading off a working instrument. It is zero because the board is young: the oldest last-touch among 457 open issues is #5157 at 2026-08-21T08:18:51Z — 18 days, against a 60-day threshold. The card's worry that a fix would turn on "eight months of accumulated sweeping in one scheduled run" is falsified: there is no accumulation. This is the cheapest possible moment to land the repair.

⚠️ But the margin expires, and it is the whole safety argument. The exempt sets in this file predate the repository's PM protocol: pm:queue, pm:on-hold and needs-user-decision are not exempt, and those are the cards parked on purpose waiting for a human. Three of the five oldest-touched open issues carry only pm:queue + domain:ui. The first quiet 60-day stretch on the board is when this job starts closing the maintainer's own decision queue. That question is not this diff's to answer — see the recommendation below.

Verification

check result
pnpm exec vitest run scripts/__tests__/ci-cd-pipeline-doc.test.ts scripts/__tests__/workflow-cache-save-bound.test.ts Test Files 2 passed (2) · Tests 60 passed (60)
node scripts/check-changeset-presence.mjs exit 0 — "1 file(s) changed, 0 of them published source … no changeset is owed" (measured, not assumed)
pnpm check:control-bytes exit 0 — 6699 tracked text files
node scripts/check-shell-escape-residue.mjs exit 0
node scripts/check-governed-queue-guard.mjs --test .github/workflows/stale.yml NOT GOVERNED

The two workflow-shape pins are the ones that could have moved: ci-cd-pipeline-doc requires stale.yml to keep a heading on the CI/CD page (it does — untouched), and workflow-cache-save-bound requires this job to keep no timeout-minutes and to keep 360 and objectui#7956 in its comments (all three preserved; the added comment sits above the steps: entry, not in the accept-360 block).

Permissions, cron, both message bodies, both exempt sets, all four day thresholds and operations-per-run are byte-identical to main.

⭐ Recommendation the maintainer has to route: retire this workflow

Not implemented here — retiring it removes a declared capability, which is not a dev seat's call. The evidence, all measured above:

  • it has failed since run Implement visual designer for Object UI schemas #1, for eight months, and nobody noticed. It is not a required status check and blocks nothing;
  • its measured output under the real policy today is exactly zero items, on a board of 466;
  • the board stays fresh because agents and the PM loop touch it daily, not because anything ages it. Aging has never contributed;
  • every issue and PR in the measured population is authored by repository members. The business case a stale bot normally serves — politely closing abandoned drive-by contributions from outside — has no population here;
  • its exempt-label vocabulary is from before the PM protocol existed, so the one population it would eventually act on is the deliberately-parked decision queue.

Retirement is not free and the costs should be named: deleting the file also requires deleting its section and inventory row on content/docs/guide/ci-cd-pipeline.md (the doc-parity pin fails in both directions) and its entry in the accept-360 table of scripts/__tests__/workflow-cache-save-bound.test.ts. AGENTS.md also records that the Actions-registry behaviour on deleting a workflow from the default branch has never happened in this repository and is untested — so the registry entry's fate is unknown, not predicted.

If retirement is chosen, this PR is superseded and can simply be closed unmerged. If it is not, this PR is the repair, and the exempt-label question becomes live within roughly six weeks.

四轴分析

方案 A —— 只修 SHA(本 PR) · 方案 B —— 退役 stale.yml · 方案 C —— 修 SHA 并同时改写豁免标签集,把 pm:* 纳入 exempt

实际业务需求。 实测而非"读起来像有用":该 job 自 run #1 起从未执行过一次,236 次全红;按真实策略实跑,处理 466 条开放条目、标记 0 条(阳性对照同一文件、阈值置 0 时报 125 条,证明读数来自可用仪器)。没有任何东西消费它的输出——它不是 required check、不阻塞任何合并。它服务的场景(自动清理外部贡献者遗弃的 issue/PR)在本仓不存在:测量到的整个开放集合的作者都是仓库成员。⇒ 这一轴指向 B。A 修好的是一台没人用的机器;C 在没人用的机器上再投入策略设计。

项目长远合理性。 本仓反复处理的正是"看起来像强制、其实不是"这一类(#3009 / #3181 / #3494),而这张卡是同一类的极端形态:声明了一套自动化,八个月一次也没跑。contract-first 的处理只有两条——要么真正兑现声明,要么删掉声明;保留一个红着的声明是两者中最差的第三条。A 兑现了声明,但兑现的是一套没人要的声明;B 删掉声明,与 ADR-0049 enforce-or-remove 的方向一致。C 是"先补丁再说":它在能力本身未被确认需要之前就去调策略参数,属于临时补丁式选项,长期代价是把一个无消费能力固化成需要长期维护的策略面。⇒ B 优,A 次,C 最差。

防 AI 写代码犯错(尤其防 AI 写元数据 app 犯错)。 这一轴上本卡有一条直接教训:e00e804f… 这个 SHA 就是一个声明了、运行时不兑现的 pin——它看起来是最严格的写法(SHA pin 优于浮动 tag),实际上是本仓唯一一个 SHA pin,也是唯一一个从未解析成功过的 action ref;仓内其余每一个 action 都用浮动 major tag(actions/checkout@v7 等),从不出事。⇒ 收紧的形式如果没有任何东西验证它,就只是更精致的幻影。本 PR 因此接受"手写一个 SHA"这条路,而是让 runner 自己打印 tag→SHA 映射再抄回来,并在注释里把这条禁令写死。就方案而言:B 让这类错误结构上不可能再犯(文件不存在);A 保留了这个面但把它变成可验证的;C 新增策略参数 = 新增可写错的面。⇒ B 优于 A 优于 C

创业阶段不扩散需求。 维护者 2026-08-04:「我们是一个创业项目,应该先专注于核心能力」。一个八个月零产出、零消费者的能力,正是"已发布零消费的能力不因沉没成本获得豁免"所指的对象——它已经写了八个月,这不构成保留理由。维护者 2026-08-27 逐字裁:「项目在创业阶段,用户也很少,短期不考虑渐进。」⇒ 退役默认立即,不设分阶段窗口;C 恰恰是分阶段过渡的形态(先留着、慢慢调策略),按这条裁决除非有具名外部用户证据否则不得作为推荐,而本卡测得的证据方向相反:整个开放集合无外部作者。⇒ 强指向 B,明确反对 C

推荐:B(退役),A 作为已落地的安全中间态。 四轴一致指向 B,无冲突需要权衡。之所以仍然提交 A:①卡片点名的缺陷是真实的,修复只有一行且经真实 runner 验证;②A 今天的实测影响恰好为 0,是代价最低的时刻;③把一个长期红着的定时任务变绿,消除的是"定时任务失败可以忽略"这种被训练出来的麻木;④若维护者裁 B,A 只需关掉 PR 不合并即可,零回滚成本。⛔ 本席位不执行 B——删除已声明能力属于维护者决定,按 dispatch 交回本座位路由。

顺带记录、未单独立卡(路由权按 dispatch 保留给派发席位): ① v9.0.0 面向 Node 20,runner 已打印 Node.js 20 is deprecated … being forced to run on Node.js 24;@v11 在同一次探测中解析成功且无此告警。是否升版应在退役决定之后再谈,否则是为一个可能被删掉的文件做迁移。② 本仓唯一的 SHA pin 就是这一个,其余 action 一律浮动 major tag——pin 策略在本仓并不统一,这本身是一个可以裁的问题。


Generated by Claude Code

…jectui#8126)

`stale.yml` pinned `actions/stale@e00e804f6792d3fedb5bd3a27df2761c5f86c981`.
That SHA is not v9.0.0's commit and is not any commit in `actions/stale`, so
every run died in `Set up job` before the action started:

    ##[error]Unable to resolve action `actions/stale@e00e804f6792d3fedb5bd3a27df2761c5f86c981`,
    unable to find version `e00e804f6792d3fedb5bd3a27df2761c5f86c981`

read verbatim from run 34172858995. Re-derived 2026-09-08: 236 completed
`schedule` runs, run #1 (21052154491, 2026-01-16T01:14:37Z) and run #236 both
`failure`. The comment beside the ref was right; only the SHA was wrong.

The replacement is measured, not looked up. A probe branch resolved the tag on
a real runner and the runner printed the mapping:

    Download action repository 'actions/stale@v9.0.0' (SHA:28ca1036281a5e5922ead5184a1bbf96e5fc984e)

Ten candidate refs were probed in one run (34173534757): `actions/checkout@v7`
green as the positive control, the pinned SHA red as the negative control,
`main` / `master` / v8 / v9 / v9.0.0 / v10 / v11 all resolving, v12 not. v9.0.0
is kept because it is the version this file already declares; a bump is a
separate decision.

Then measured what turning it on does, before turning it on. This file with
only the SHA changed, plus `debug-only: true` and a read-only token, ran GREEN
(34173608021) -- the first success in this workflow's life -- and swept the
open board to exhaustion: 466 items (457 issues, 9 PRs) and a `Statistics:`
block with no stale, label or comment counter at all. Nothing marked, nothing
closed. Control on the same file the same day (34173673214), thresholds
dropped to 0: `New stale items: 125`, `Added items labels: 125`, `Added items
comments: 125`. The zero is a reading, not a silent instrument. It is zero
because the board is young -- oldest last-touch among 457 open issues is #5157
at 2026-08-21T08:18:51Z, 18 days against a 60-day threshold.

One line of behaviour changes: the SHA. Policy, permissions, cron and the
objectui#7956 accept-360 comment block are untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
baozhoutao pushed a commit that referenced this pull request Sep 8, 2026
⛔ THIS BRANCH IS EVIDENCE, NOT A CLAIM. It is not a second seat working
objectui#8126 and it must never be merged. The card's claim and its fix live on
`claude/issue-8126-stale-yml` (PR #8463).

The three dispatch-only runs it produced, all dry runs that wrote nothing
(`debug-only: true`, read-only token):

  34173534757  ten candidate action refs, one job each, with a positive and a
               negative control
  34173608021  the real policy with the corrected SHA — first green run in this
               workflow's life; 466 open items swept, 0 marked
  34173673214  positive control for that zero: thresholds at 0 report
               `New stale items: 125`

`stale.yml` here is now byte-identical to origin/main again, so nothing can be
dispatched off this branch by accident. The remote ref cannot be deleted from
an agent container (AGENTS.md: ref deletions are refused with HTTP 403 and git
still prints `Everything up-to-date`), which is why this commit exists instead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
@claude

claude Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

⛔ HELD in draft by the domain:devx @ objectui execution seat (PM session session_01FhBNJcLRZLe8M87VcUgpKr, R46, 2026-09-08T01:02Z). ⛔ Not a rejection — the work was reviewed and accepted on objectui#8126 (comment).

The root cause is proven, the replacement SHA was obtained by having a runner print the mapping rather than by hand, and both controls fire in the same probe run (control_checkout green, control_broken_sha red, stale_v12 red) — all re-read off GitHub by this seat. Independent write control: GET /labels/stale returns 404, so the stale label does not exist in this repository; actions/stale creates it when it applies it, which is proof the threshold-0 probe wrote nothing despite reporting Added items labels: 125.

Why it is not being flipped ready: the file that was measured is not the file that lands. The probes carried debug-only: true and a read-only token; this diff does not. Merging arms the nightly job for real, and the job's exempt sets — pinned,security,critical,bug,enhancement for issues, pinned,security,in-progress,blocked for PRs — contain no pm:* label and no needs-user-decision. Those are the cards this repository's PM protocol parks on purpose waiting for a human. Today the job would mark 0 items and there is roughly 42 days of headroom before the first candidate exists; landing now would make that safe state depend on a decision arriving inside that window.

⇒ Two declared contracts conflict, so this seat acts on the stricter one and routes rather than deciding. objectui#8126 now carries pm:retriage with the fork written out: A land as-is, B retire the workflow, C land it and exempt the pm:* labels in the same change. The dev recommends B with A as the interim; ⛔ neither the dev nor this seat answers a capability question.

Nothing is owed by this PR while it waits — no rebase, no changes requested. When the answer comes back as A or C this goes straight to CI → ready → queue; if it comes back as B, this PR is superseded and closes unmerged at zero rollback cost, which the report already anticipated.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[ci] stale.yml has never succeeded: 234 scheduled runs, 234 failures, every one in Set up job before actions/stale starts

2 participants